package com.haoqizhe.kernel.security.config;

import cn.hutool.json.JSONUtil;
import com.haoqizhe.kernel.jwt.utils.JwtTokenUtil;
import com.haoqizhe.kernel.commons.constants.HeaderConstants;
import com.haoqizhe.kernel.commons.enums.ResultCode;
import com.haoqizhe.kernel.commons.util.SpringContextUtil;
import com.haoqizhe.kernel.core.entity.LoginUserParams;
import com.haoqizhe.kernel.web.result.DefaultErrorResult;
import com.haoqizhe.kernel.web.result.Result;
import com.haoqizhe.kernel.security.entity.ISecurityUser;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/**
 * 拦截登录，登录成功 与 失败的处理
 *
 * @author haoqizhe.li
 * @date 2018/10/15
 **/
@Slf4j
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private ThreadLocal<String> rememberMe = new ThreadLocal<>();
    private AuthenticationManager authenticationManager;

    public JwtAuthenticationFilter(AuthenticationManager authenticationManager, String processesUrl) {
        this.authenticationManager = authenticationManager;
        super.setFilterProcessesUrl(processesUrl);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request,
                                                HttpServletResponse response) throws AuthenticationException {
        // 从输入流中获取到登录的信息
        try {
            LoginUserParams user = new ObjectMapper().readValue(request.getInputStream(), LoginUserParams.class);
            rememberMe.set(user.getRememberMe());
            return authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(user.getAccount(), user.getPassword(), new ArrayList<>())
            );
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            return null;
        }
    }

    /**
     * <p>验证成功后处理</p>
     *
     * @param request
     * @param response
     * @param chain
     * @param authResult
     * @return
     * @author haoqizhe.li
     * @date 2018/10/16
     **/
    @Override
    protected void successfulAuthentication(HttpServletRequest request,
                                            HttpServletResponse response,
                                            FilterChain chain,
                                            Authentication authResult) throws IOException, ServletException {

        ISecurityUser jwtUser = (ISecurityUser) authResult.getPrincipal();

        boolean isRemember = "1".equals(rememberMe.get());
        rememberMe.remove();

        String role = "";
        Collection<? extends GrantedAuthority> authorities = jwtUser.getAuthorities();
        for (GrantedAuthority authority : authorities) {
            role = authority.getAuthority();
        }

        JwtTokenUtil jwtTokenUtil = SpringContextUtil.getBean(JwtTokenUtil.class);

        Map<String, Object> map = new HashMap<>(0);
        map.put("role", role);

        String token = jwtTokenUtil.createToken(jwtUser.getUsername(), map, isRemember);
//        log.info("===================================token 创建时间===============================");
//        log.info(DateUtils.format(jwtTokenUtil.getCreateDate(token), DateUtils.SDF_YMDHMS));
//        log.info("===================================token ===============================");
//        log.info(token);
//        log.info("===================================token 失效时间===============================");
//        log.info(DateUtils.format(jwtTokenUtil.getExpiration(token), DateUtils.SDF_YMDHMS));

        // 返回创建成功的token
        // 但是这里创建的token只是单纯的token
        // 按照jwt的规定，最后请求的时候应该是 `Bearer token
        response.setHeader(HeaderConstants.X_TOKEN, HeaderConstants.TOKEN_PREFIX + token);
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.getWriter().write(JSONUtil.toJsonPrettyStr(Result.success(jwtUser)));
    }

    /**
     * <p>验证不成功后处理</p>
     *
     * @param request
     * @param response
     * @param e
     * @return
     * @author haoqizhe.li
     * @date 2018/10/16
     **/
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
        log.error(e.getMessage(), e);

        ResultCode code = ResultCode.PERMISSION_NO_ACCESS;
        HttpStatus status = HttpStatus.BAD_REQUEST;
        //用户不存在
        if (e instanceof UsernameNotFoundException) {
            code = ResultCode.USER_NOT_EXIST;
            //账号或者密码错误
        } else if (e instanceof BadCredentialsException) {
            code = ResultCode.USER_LOGIN_ERROR;
            //用户被锁定，SecurityUser.isAccountNonLocked =  false 时触发
        } else if (e instanceof LockedException) {
            code = ResultCode.USER_ACCOUNT_LOCKED;
            //用户凭证过期，SecurityUser.isCredentialsNonExpired =  false 时触发
        } else if (e instanceof CredentialsExpiredException) {
            code = ResultCode.USER_CREDENTIALS_EXPIRED;
            //用户被禁用，SecurityUser.isEnabled =  false 时触发
        } else if (e instanceof DisabledException) {
            code = ResultCode.USER_ACCOUNT_FORBIDDEN;
            //用户不在有效期内，SecurityUser.isAccountNonExpired =  false 时触发
        } else if (e instanceof AccountExpiredException) {
            code = ResultCode.USER_IS_NOT_VALID_DATE;
        } else {
            status = HttpStatus.UNAUTHORIZED;
        }
        response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
        response.setStatus(status.value());
        response.getWriter().write(JSONUtil.toJsonPrettyStr(DefaultErrorResult.failure(code, e, status)));
    }

}
